package com.yugao.fintech.antelope.infra.weixin.mp.service.impl;

import cn.hutool.core.collection.CollUtil;
import cn.hutool.core.util.StrUtil;
import com.baomidou.mybatisplus.extension.service.IService;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.github.yitter.idgen.YitIdHelper;
import com.yugao.fintech.antelope.base.exception.enums.GlobalErrorEnum;
import com.yugao.fintech.antelope.infra.weixin.mp.assembly.MpMenuAssembly;
import com.yugao.fintech.antelope.infra.weixin.mp.constants.WxCons;
import com.yugao.fintech.antelope.infra.weixin.mp.handler.WxMpHandler;
import com.yugao.fintech.antelope.infra.weixin.mp.manager.MpMenuManager;
import com.yugao.fintech.antelope.infra.weixin.mp.mapper.MpMenuMapper;
import com.yugao.fintech.antelope.infra.weixin.mp.model.WxMpContext;
import com.yugao.fintech.antelope.infra.weixin.mp.model.entity.MpMenu;
import com.yugao.fintech.antelope.infra.weixin.mp.model.rqrs.MpMenuReq;
import com.yugao.fintech.antelope.infra.weixin.mp.model.rqrs.MpMessageSendOutReq;
import com.yugao.fintech.antelope.infra.weixin.mp.service.MpMenuService;
import com.yugao.fintech.antelope.infra.weixin.mp.service.MpMessageService;
import com.yugao.fintech.antelope.infra.weixin.mp.utils.MpUtils;
import com.yugao.fintech.antelope.base.model.module.config.param.WxMpParam;
import com.yugao.fintech.framework.assistant.utils.exception.BizException;
import lombok.extern.slf4j.Slf4j;
import me.chanjar.weixin.common.bean.menu.WxMenu;
import me.chanjar.weixin.common.bean.menu.WxMenuButton;
import me.chanjar.weixin.common.error.WxErrorException;
import me.chanjar.weixin.mp.bean.message.WxMpXmlOutMessage;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import javax.validation.Validator;
import java.util.*;
import java.util.stream.Collectors;

/**
 * 公众号菜单
 *

 */
@Slf4j
@Service
public class MpMenuServiceImpl extends ServiceImpl<MpMenuMapper, MpMenu>
        implements IService<MpMenu>, MpMenuService {

    @Autowired
    private Validator validator;

    @Autowired
    private MpMenuManager mpMenuManager;
    @Autowired
    private MpMenuAssembly mpMenuAssembly;
    @Autowired
    private WxMpHandler wxMpHandler;
    @Autowired
    private MpMessageService mpMessageService;

    @Override
    @Transactional(rollbackFor = Exception.class)
    public void releaseMenu(MpMenuReq req) {
        WxMpParam mpParam = wxMpHandler.getContext().getMpParam();
        // 构建的微信菜单
        WxMenu wxMenu = new WxMenu();

        List<MpMenuReq.Menu> menus = req.getMenus();
        List<MpMenu> newMenu = new ArrayList<>();
        for (MpMenuReq.Menu menu : menus) {
            validateMenu(menu);
            Long id = YitIdHelper.nextId();
            menu.setId(id);
            menu.setParentId(WxCons.MENU_ROOT_PARENT_ID);

            MpMenu mpMenu = mpMenuAssembly.toMpMenu(menu);
            newMenu.add(mpMenu);

            // 微信一级菜单
            WxMenuButton wxButton = mpMenuAssembly.toWxMenu(mpMenu);
            wxMenu.getButtons().add(wxButton);

            Optional.ofNullable(menu.getChildren()).orElse(Collections.emptyList()).forEach(e -> {
                e.setId(YitIdHelper.nextId());
                e.setParentId(id);
                MpMenu mpSubMenu = mpMenuAssembly.toMpMenu(e);
                newMenu.add(mpSubMenu);

                // 微信二级菜单
                WxMenuButton wxSubButton = mpMenuAssembly.toWxMenu(mpSubMenu);
                wxButton.getSubButtons().add(wxSubButton);
            });
        }

        // 先删除后插入
        this.remove(MpMenu.lqw());
        List<MpMenu> saveMenus = newMenu.stream().peek(e -> e.setMpAppId(mpParam.getAppId())).collect(Collectors.toList());
        checkMenu(saveMenus);
        this.saveBatch(saveMenus);

        // 同步到微信
        WxMpContext context = wxMpHandler.getContext();
        me.chanjar.weixin.mp.api.WxMpService service = context.getService();
        try {
            service.getMenuService().menuCreate(wxMenu);
        } catch (WxErrorException e) {
            log.error("error: {}", e.getMessage());
            throw new BizException(e.getError().getErrorMsg());
        }
    }

    private void checkMenu(List<MpMenu> menus) {
        List<String> repeatMenuKeys = new ArrayList<>();
        Map<String, List<MpMenu>> collect = menus.stream().collect(Collectors.groupingBy(MpMenu::getMenuKey));
        collect.forEach((k, v) -> {
            if (v.size() > 1) {
                repeatMenuKeys.add(k);
            }
        });
        if (!repeatMenuKeys.isEmpty()) {
            throw new BizException(String.join(",", repeatMenuKeys) + " 重复");
        }
    }

    @Override
    public List<MpMenu> listMenu() {
        return this.list();
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public void deleteMenu() {
        if (!this.remove(MpMenu.lqw())) {
            throw new BizException(GlobalErrorEnum.OPERATION_FAIL_DELETE);
        }

        WxMpContext context = wxMpHandler.getContext();
        me.chanjar.weixin.mp.api.WxMpService service = context.getService();
        try {
            service.getMenuService().menuDelete();
        } catch (WxErrorException e) {
            log.error("error: {}", e.getMessage());
            throw new BizException(e.getError().getErrorMsg());
        }
    }

    @Override
    public WxMpXmlOutMessage reply(WxMpParam mpParam, String menuKey, String openId) {
        // 第一步，获得菜单
        MpMenu menu = mpMenuManager.getByAppIdAndMenuKey(mpParam.getAppId(), menuKey);
        if (menu == null) {
            log.error("[reply][appId({}) key({}) 找不到对应的菜单]", mpParam.getAppId(), menuKey);
            return null;
        }
        // 按钮必须要有消息类型，不然后续无法回复消息
        if (StrUtil.isEmpty(menu.getReplyMessageType())) {
            log.error("[reply][menu({}) 不存在对应的消息类型]", menu);
            return null;
        }

        // 第二步，回复消息
        MpMessageSendOutReq rs = mpMenuAssembly.toMpMessageSendOutRS(openId, menu);
        return mpMessageService.sendOutMessage(rs);
    }

    /**
     * 校验菜单的格式是否正确
     *
     * @param menu 菜单
     */
    private void validateMenu(MpMenuReq.Menu menu) {
        MpUtils.validateButton(validator, menu.getType(), menu.getReplyMessageType(), menu);
        // 子菜单
        if (CollUtil.isEmpty(menu.getChildren())) {
            return;
        }
        menu.getChildren().forEach(this::validateMenu);
    }
}
